#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <libgen.h>
#include <dirent.h>
#include <pthread.h>
#include <semaphore.h>
#include <assert.h>
#include <ctype.h>

#include <arpa/inet.h>

#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/vfs.h>
#include <sys/mount.h>

#include <linux/unistd.h>
#include <linux/if.h>
#include <linux/wireless.h>

#include <net/route.h>

#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>

#include "bb_api.h"
#include "prj_rpc.h"
#include "getopt.h"

#define MCS_CFG_DEFUALT  -2
#define MCS_CFG_AUTO     -1

#define PWR_CFG_DEFUALT  -2
#define PWR_CFG_AUTO     -1

#define CHAN_CFG_DEFUALT  -2
#define CHAN_CFG_AUTO     -1

#define COMPLIANCE_MODE_DEFUALT  -1

static bb_dev_handle_t *bb_handle;
static int slot = 0;//-1;   //default slot 0
static int ioctl_slot = -1;

void bb_test_Usage(char * name)
{
    printf("Usage: %s [options]\n", name);
    printf("Options:\n");
    printf("\t-h       Print help info \n");
    printf("\t-d       DEV mode\n");
    printf("\t-a       AP mode\n");
    printf("\t-s       Slot id\n");
    printf("\t-S       ioctl slot id\n");
    printf("\t-b       Bandwidth,refers to bb_bandwidth_e\n");
    printf("\t-m       MCS, refers to bb_phy_mcs_e. -1 means auto.\n");
    printf("\t-P       Set power, -1 means auto\n");
    printf("\t-c       Set chan, -1 means auto\n");
    printf("\t-g       get info\n");
    printf("\t-C       Set compliance_mode\n");
    printf("\t-r       Remote role\n");
    printf("\t-G       get cfg\n");
    printf("\t-R       reset cfg\n");
    printf("\t-o       reboot \n");
}

int ar8030_ioctl(bb_dev_handle_t* pdev, uint32_t request, const void* in, uint16_t in_len, void* out, uint16_t out_len)
{
    int ret = 0;
    bb_remote_ioctl_in_t remote_in = {0};
    bb_remote_ioctl_out_t remote_out = {0};

    if (ioctl_slot < 0) {
        ret = bb_ioctl(pdev, request, in, out);
    }
    else {
        memcpy(remote_in.data, in, in_len);
        remote_in.len = in_len;
        remote_in.slot = ioctl_slot;
        remote_in.msg_id = request;

        ret = bb_ioctl(pdev, BB_REMOTE_IOCTL_REQ, &remote_in, &remote_out);
        if (out) {
            memcpy(out, remote_out.data, out_len);
        }
    }

    return ret;
}


void bb_reboot()
{
     bb_set_reboot_t reboot;
    printf("cfg end, reboot!\n");
    reboot.tim_ms = 0;
    bb_ioctl(bb_handle, BB_SET_SYS_REBOOT, &reboot, NULL);

    printf("test end, exit!\n");

 
      
}
void bb_info_dump()
{
    int ret;
    int i;

    //get status
    bb_get_1v1_info_in_t one_by_one_info_in = {0};
    bb_get_1v1_info_out_t one_by_one_info_out = {0};

    bb_get_chan_info_out_t chan_info_out;

    bb_get_mcs_in_t mcs_in = { 0 };
    bb_get_mcs_out_t mcs_out = { 0 };

    printf("bb info dump\n");

    //get 1v1 wireless quality
    ret = ar8030_ioctl(bb_handle, BB_GET_1V1_INFO, &one_by_one_info_in, sizeof(one_by_one_info_in), &one_by_one_info_out, sizeof(one_by_one_info_out));
    if(ret){
        printf("[Ioctl Fail]BB_GET_1V1_INFO ERROR,%d !\n", ret);
    }else{
        printf("self-->snr:%d,ldpc_tlv_err :%d, ldpc_num_err:%d,gain_a:%d, gain_b:%d,tx_mcs:%d,tx_chan:%d,tx_power:%d,lna_inner_bypass %d,lna_fem_bypass %d\n",
                one_by_one_info_out.self.snr,
                one_by_one_info_out.self.ldpc_tlv_err_ratio,
                one_by_one_info_out.self.ldpc_num_err_ratio,
                one_by_one_info_out.self.gain_a,
                one_by_one_info_out.self.gain_b,
                one_by_one_info_out.self.tx_mcs,
                one_by_one_info_out.self.tx_chan,
                one_by_one_info_out.self.tx_power,
                one_by_one_info_out.self.lna_inner_bypass,
                one_by_one_info_out.self.lna_fem_bypass);
        printf("peer-->snr:%d,ldpc_tlv_err :%d, ldpc_num_err:%d,gain_a:%d, gain_b:%d,tx_mcs:%d,tx_chan:%d,tx_power:%d,lna_inner_bypass %d,lna_fem_bypass %d\n",
                one_by_one_info_out.peer.snr,
                one_by_one_info_out.peer.ldpc_tlv_err_ratio,
                one_by_one_info_out.peer.ldpc_num_err_ratio,
                one_by_one_info_out.peer.gain_a,
                one_by_one_info_out.peer.gain_b,
                one_by_one_info_out.peer.tx_mcs,
                one_by_one_info_out.peer.tx_chan,
                one_by_one_info_out.peer.tx_power,
                one_by_one_info_out.peer.lna_inner_bypass,
                one_by_one_info_out.peer.lna_fem_bypass);
    }
    //channel info
    ret = ar8030_ioctl(bb_handle, BB_GET_CHAN_INFO, NULL, 0, &chan_info_out, sizeof(bb_get_chan_info_out_t));
    if (ret) {
        printf("[Ioctl Fail]BB_GET_CHAN_INFO ERR, %d !\n", ret);
    }
    else {
        printf("chan_num %d auto_mode %d acs_chan %d work_chan %d\n",
            chan_info_out.chan_num,
            chan_info_out.auto_mode,
            chan_info_out.acs_chan,
            chan_info_out.work_chan);
        for(i=0; i<chan_info_out.chan_num; i++){
            printf("chan %d freq %d power %d\n", i, chan_info_out.freq[i], chan_info_out.power[i]);
        }
    }

    //Get MCS and cur max throughput
    mcs_in.slot = 0;
    mcs_in.dir = BB_DIR_RX;
    ret = ar8030_ioctl(bb_handle, BB_GET_MCS, &mcs_in, sizeof(mcs_in), &mcs_out, sizeof(mcs_out));
    if (ret)
    {
        printf("[Ioctl Fail]BB_GET_MCS ERR, %d !\n", ret);
    }
    else{
        printf("mcs %d TP %d(kbps)\n", mcs_out.mcs, mcs_out.throughput);
    }

}
void bb_cfg_reset()
{

       bb_ioctl(bb_handle, BB_RESET_CFG, NULL,NULL );
 
      
}

void bb_get_version()
{

    bb_get_sys_info_out_t bb_get_sys_info_out;
    int s32Ret = bb_ioctl(bb_handle, BB_GET_SYS_INFO, NULL, &bb_get_sys_info_out);
    if (s32Ret) {
    printf("BB_GET_SYS_INFO failed s32Ret = %d\n", s32Ret);
    } 
    printf("firmware_ver : %s\n", bb_get_sys_info_out.firmware_ver); 
}

void bb_cfg_dump()
{
    int ret;
    int i = 0;
    int buffLen = 100*1024;
    uint8_t *getData;

    bb_get_cfg_out_t out = {};
    bb_get_cfg_in_t in = {0, 0, 0, 0, sizeof(out.data)-1};
    in.mode = 0;

    getData = malloc(100*1024);
    memset(getData, 0, 100*1024);

    do
    {
        int ret = ar8030_ioctl(bb_handle, BB_GET_CFG, &in, sizeof (bb_get_cfg_in_t), &out, sizeof (bb_get_cfg_out_t));
        if (ret)
        {
            printf("get cfg err! ret %d i %d\n", ret, i);
            return;
        }
        if (in.offset + out.length > buffLen)
        {
            printf("buf overflow !!!!!!!\n");
            return;
        }
        printf("%d:%s",i,out.data);
        ++i;
        memcpy(getData + in.offset, out.data, out.length);
        in.offset += out.length;
        in.seq = i;
        out.data[out.length] = 0;
        memset(out.data, 0, sizeof(out.data));
    } while (out.length > 0);

    printf("get cfg:\n");
    printf("%s\n", getData);

    free(getData);
}

// 新增函数：设置频段
void bb_set_band(int band_value)
{
    uint8_t band = 0x07; // 默认Auto
    
    // 根据输入参数设置频段
    switch(band_value) {
        case 0:
            band = 0x02; // 2G
            printf("Setting band to 2G\n");
            break;
        case 1:
            band = 0x04; // 5G
            printf("Setting band to 5G\n");
            break;
        case 2:
            band = 0x07; // Auto
            printf("Setting band to Auto\n");
            break;
        default:
            printf("Invalid band value: %d. Using default (Auto)\n", band_value);
            break;
    }
    
    bb_set_prj_dispatch_in_t set_prj;
    memset(&set_prj, 0, sizeof(set_prj));
    prj_rpc_hdr_t* hdr = (prj_rpc_hdr_t*)set_prj.data;
    hdr->cmdid = PRJ_CMD_SET_BAND;
    prj_cmd_set_band_t *set_band = (prj_cmd_set_band_t *)hdr->data;
    set_band->band_bmp = band;
    
    int ret = ar8030_ioctl(bb_handle, BB_SET_PRJ_DISPATCH, &set_prj, sizeof(set_prj), NULL, 0);
    if (ret) {
        printf("Set band failed! Error code: %d\n", ret);
    } else {
        printf("Set band successfully!\n");
    }
}

int main(int argc, char ** argv)
{
    int s32Ret;
    int ret;
    bool is_ap = false;
    bool is_frame_change = false;
    bool is_tx = true;
    bool is_getinfo = false;
    bool is_reboot = false;
    
    bool is_getcfg = false;
    bool is_resetcfg = false;
    bool is_get_version = false;
    int band_value = -1; // 新增：频段参数，默认-1表示不设置
    

    int i;
    int dev_num;

    bb_host_t* phost=NULL;
    bb_dev_t** devs=NULL;

    bb_set_mcs_mode_t bb_set_mcs_mode;
    bb_set_mcs_t bb_set_mcs;
    bb_set_bandwidth_t bb_set_bandwidth;
    bb_set_pwr_auto_in_t bb_set_pwr_auto_in;
    bb_set_pwr_mode_in_t bb_set_pwr_mode_in;
    bb_set_pwr_in_t bb_set_pwr_in;
    bb_set_chan_mode_t chan_mode;
    bb_set_chan_t set_chan;

    int power = PWR_CFG_DEFUALT;
    int mcs = MCS_CFG_DEFUALT;
    int chan = CHAN_CFG_DEFUALT;
    int compliance_mode = COMPLIANCE_MODE_DEFUALT;
    int role = -1;
    int bandwidth = 0;

    if(argc == 1){
        bb_test_Usage(argv[0]);
        return -1;
    }

    while((s32Ret = getopt(argc, argv, "hdas:S:ym:b:P:c:gC:r:GRvoq:")) != -1)
    {
        switch(s32Ret)
        {
            case 'h':
                bb_test_Usage(argv[0]);
                return 0;
            case 'd':
                printf("dev mode\n");
                is_ap = false;
                break;
            case 'a':
                printf("ap mode\n");
                is_ap = true;
                break;
            case 'g':
                printf("get info\n");
                is_getinfo = true;
                break;
            case 'o':
                printf("reboot ...\n");
                is_reboot = true;
                break;
            case 's':
                slot = atoi(optarg);
                printf("slot %d\n", slot);
                break;
            case 'S':
                ioctl_slot = atoi(optarg);
                printf("ioctl_slot %d\n", ioctl_slot);
                break;
            case 'm':
                mcs = atoi(optarg);
                printf("mcs = %d\n",mcs);
                break;
            case 'b':
                bandwidth = atoi(optarg);
                printf("bandwidth = %d\n",bandwidth);
                break;
            case 'P':
                power = atoi(optarg);
                printf("power = %d\n",power);
                break;
            case 'c':
                chan = atoi(optarg);
                printf("chan = %d\n",chan);
                break;
            case 'C':
                compliance_mode = atoi(optarg);
                printf("compliance_mode = %d\n",compliance_mode);
                break;
            case 'r':
                role = atoi(optarg);
                printf("cool remote role = %d\n",role);
                break;
            case 'G':
                is_getcfg = true;
                printf("get cfg\n");
                break;
            case 'R':
                is_resetcfg = true;
                printf("reset cfg\n");
                break;
            case 'v':
                is_get_version = true;
                printf("get version\n");
                break;
            case 'q':  // 新增：处理频段设置
                band_value = atoi(optarg);
                printf("band = %d\n", band_value);
                break;
            default:
                printf("unknow cmd param !\n");
                bb_test_Usage(argv[0]);
                return -1;
        }
    }


    /* ==================Build connection with 8030(start)================ */
    /* Connect to the daemon */
    s32Ret = bb_host_connect(&phost,"127.0.0.1",BB_PORT_DEFAULT);
    if(s32Ret)
    {
        printf("bb connect error,ret=%d\n",s32Ret);
        //bb_deinit();
        return -1;
    }

    /* get dev list of the 8030 */
    dev_num = bb_dev_getlist(phost,&devs);
    if (dev_num == 0) {
        printf("Get no device!\n");
        return -1;
    }

    /* Open the dev 8030 */
    bb_handle = bb_dev_open(devs[0]);
    if (!bb_handle) {
        printf("bb_dev_open error\n");
        //bb_deinit();
        return -1;
    }

    bb_dev_freelist(devs);

    // 新增：设置频段
    if (band_value >= 0) {
        bb_set_band(band_value);
    }
    if (is_getinfo) {
        bb_info_dump();
    }
    if (is_reboot) {
        bb_reboot();
    }

    if (is_getcfg) {
        bb_cfg_dump();
    }
    if (is_resetcfg) {
        bb_cfg_reset();
    }
    if (is_get_version) {
        bb_get_version();
    }
  
    if (power == PWR_CFG_AUTO) {
        /* power auto */
        bb_set_pwr_auto_in.pwr_auto = 1;
        ar8030_ioctl(bb_handle, BB_SET_POWER_AUTO, &bb_set_pwr_auto_in, sizeof(bb_set_pwr_auto_in), NULL, 0);
    }
    else if (power >= 0) {
        /* power auto */
        bb_set_pwr_auto_in.pwr_auto = 0;
        ar8030_ioctl(bb_handle, BB_SET_POWER_AUTO, &bb_set_pwr_auto_in, sizeof(bb_set_pwr_auto_in), NULL, 0);

        /* power low */
        bb_set_pwr_in.usr = slot;//BB_USER_BR_CS;
        bb_set_pwr_in.pwr = power;
        ar8030_ioctl(bb_handle, BB_SET_POWER, &bb_set_pwr_in, sizeof(bb_set_pwr_in), NULL, 0);
    }
    /* set bandwidth*/
    if (bandwidth) {
        bb_set_bandwidth.bandwidth = bandwidth;
        if (is_tx) {
            bb_set_bandwidth.dir = BB_DIR_TX;
        }
        else {
            bb_set_bandwidth.dir = BB_DIR_RX;
        }

        bb_set_bandwidth.slot = slot;
        ret = ar8030_ioctl(bb_handle, BB_SET_BANDWIDTH, &bb_set_bandwidth, sizeof(bb_set_bandwidth), NULL, 0);
        printf("BB_SET_BANDWIDTH bandwidth %d ret %d\n", bandwidth, ret);
        usleep(2000*1000);
    }

    /*set mcs*/
    if (mcs == MCS_CFG_AUTO) {
        bb_set_mcs_mode.auto_mode = true;
        bb_set_mcs_mode.slot = slot;
        s32Ret = ar8030_ioctl(bb_handle, BB_SET_MCS_MODE, (void*)&bb_set_mcs_mode, sizeof(bb_set_mcs_mode), NULL, 0);
        if (s32Ret) {
            printf("set mcs mode err! ret = %d\n",s32Ret );
            return -1;
        }
        usleep(2000*1000);
    }
    else if (mcs >= 0){
        bb_set_mcs_mode.auto_mode = false;
        bb_set_mcs_mode.slot = slot;
        s32Ret = ar8030_ioctl(bb_handle, BB_SET_MCS_MODE, (void*)&bb_set_mcs_mode, sizeof(bb_set_mcs_mode), NULL, 0);
        if (s32Ret) {
            printf("set mcs mode err! ret = %d\n",s32Ret );
            return -1;
        }

        bb_set_mcs.mcs = mcs;
        bb_set_mcs.slot = 0;
        s32Ret = ar8030_ioctl(bb_handle, BB_SET_MCS, (void*)&bb_set_mcs, sizeof(bb_set_mcs), NULL, 0);
        if (s32Ret) {
            printf("set mcs err! ret = %d\n",s32Ret );
            return -1;
        }
        usleep(2000*1000);
    }

    /*set chan*/
    if (chan == CHAN_CFG_AUTO) {
        chan_mode.auto_mode = 1;
        s32Ret = ar8030_ioctl(bb_handle, BB_SET_CHAN_MODE,  &chan_mode, sizeof(chan_mode), NULL, 0);
        if (s32Ret) {
            printf("set mcs mode err! ret = %d\n",s32Ret );
            return -1;
        }

        bb_set_remote_t set_remote = {0};
        set_remote.slot = BB_SLOT_0;
        set_remote.type_bmp = (1<<BB_REMOTE_TYPE_CHAN_MODE)|(1<<BB_REMOTE_TYPE_TARGET_CHAN);
        set_remote.setting.auto_chan = chan_mode.auto_mode;
        set_remote.setting.target_chan = 0;
        ret = ar8030_ioctl(bb_handle, BB_SET_REMOTE, &set_remote, sizeof(set_remote), NULL, 0);
        if(ret){
            printf("[Ioctl Fail]BB_SET_REMOTE ERR, %d !\n",ret);
            return -2;
        }
        usleep(2000*1000);
    }
    else if (chan > 0) {
        chan_mode.auto_mode = 0;
        s32Ret = ar8030_ioctl(bb_handle, BB_SET_CHAN_MODE,  &chan_mode, sizeof(chan_mode), NULL, 0);
        if (s32Ret) {
            printf("set mcs mode err! ret = %d\n",s32Ret );
            return -1;
        }

        set_chan.chan_dir = BB_DIR_RX;
        set_chan.chan_index = chan;
        s32Ret = ar8030_ioctl(bb_handle, BB_SET_CHAN, &set_chan, sizeof(set_chan), NULL, 0);
        if(s32Ret){
            printf("[Ioctl Fail]BB_SET_CHAN RX ERR, %d !\n",s32Ret);
            return -2;
        }

        bb_set_remote_t set_remote = {0};
        set_remote.slot = BB_SLOT_0;
        set_remote.type_bmp = (1<<BB_REMOTE_TYPE_CHAN_MODE)|(1<<BB_REMOTE_TYPE_TARGET_CHAN);
        set_remote.setting.auto_chan = chan_mode.auto_mode;
        set_remote.setting.target_chan = set_chan.chan_index;
        ret = ar8030_ioctl(bb_handle, BB_SET_REMOTE, &set_remote, sizeof(set_remote), NULL, 0);
        if(ret){
            printf("[Ioctl Fail]BB_SET_REMOTE ERR, %d !",ret);
            return -2;
        }
        usleep(2000*1000);
    }

    /* set compliance_mode */
    if (compliance_mode >= 0) {
        bb_set_remote_t bb_set_remote;
        bb_set_remote.slot = slot;
        bb_set_remote.type_bmp = (1 << BB_REMOTE_TYPE_COMPLIANCE_MODE);
        bb_set_remote.setting.compliance_mode = compliance_mode;
        ar8030_ioctl(bb_handle, BB_SET_REMOTE, &bb_set_remote, sizeof(bb_set_remote), NULL, 0);

        bb_set_compliance_mode_t bb_set_compliance_mode;
        bb_set_compliance_mode.enable = compliance_mode;
        ar8030_ioctl(bb_handle, BB_SET_COMPLIANCE_MODE, &bb_set_compliance_mode, sizeof(bb_set_compliance_mode), NULL, 0);
        usleep(2000*1000);
    }

    if (role >= 0)
    {
        prj_rpc_hdr_t *hdr, *hdr_out;
        prj_cmd_set_role_t *set_role;
        bb_set_prj_dispatch_in_t set_prj = {0};

        hdr=(prj_rpc_hdr_t *)set_prj.data;
        hdr->cmdid = PRJ_CMD_SET_ROLE;

        set_role = (prj_cmd_set_role_t*)hdr->data;
        set_role->role = role;

        s32Ret = ar8030_ioctl(bb_handle, BB_SET_PRJ_DISPATCH, &set_prj, sizeof(set_prj), NULL, 0);
        if (s32Ret) {
            printf("set role err! ret = %d",s32Ret);
        }
        else {
            printf("set role %d success!\n", role);
        }

        /* get role */
        bb_get_prj_dispatch_in_t get_prj_in = {0};
        bb_get_prj_dispatch_out_t get_prj_out = {0};
        prj_cmd_get_role_t *get_role = NULL;

        hdr=(prj_rpc_hdr_t *)get_prj_in.data;
        hdr->cmdid = PRJ_CMD_GET_ROLE;

        s32Ret = ar8030_ioctl(bb_handle, BB_GET_PRJ_DISPATCH, &get_prj_in, sizeof(get_prj_in), &get_prj_out, sizeof(get_prj_out));
        if (s32Ret) {
            printf("get role err! ret = %d",s32Ret);
        }
        else {
            hdr_out=(prj_rpc_hdr_t *)get_prj_out.data;

            get_role = (prj_cmd_get_role_t*)hdr_out->data;

            printf("get role success! role is %s\n", (get_role->role == 0)?"AP":"DEV");
        }
    }
  

    return 0;
}
